
// Copyright (C) 2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Diagnostics;

using Derm.OpenGL;

namespace Derm.Render
{
	/// <summary>
	/// RenderPipeline viewport.
	/// </summary>
	public class RenderViewport
	{
		/// <summary>
		/// RenderViewport constructor. 
		/// </summary>
		/// <param name="x1">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="y1">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="x2">
		/// A <see cref="System.Single"/>
		/// </param>
		/// <param name="y2">
		/// A <see cref="System.Single"/>
		/// </param>
		public RenderViewport(float x1, float y1, float x2, float y2)
		{
			if ((x1 < 0.0f) || (x1 > 1.0f))
				throw new ArgumentException("left coordinate out of range [0.0f,1.0f]", "x1");
			if ((y1 < 0.0f) || (y1 > 1.0f))
				throw new ArgumentException("left coordinate out of range [0.0f,1.0f]", "y1");
			if (x1 > x2)
				throw new ArgumentException("left coordinate greater than right coordinate");
			if (y1 > y2)
				throw new ArgumentException("bottom coordinate greater than top coordinate");
			
			mLeft = x1; mRight = x2;
			mBottom = y1; mTop = y2;
		}

		/// <summary>
		/// Apply this RenderViewport to a RenderSurface.
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> on which the viewport is applied.
		/// </param>
		public void ApplyViewport(RenderSurface rSurface)
		{
			if (rSurface == null)
				throw new ArgumentNullException("rSurface");

			// Define viewport
			Gl.Viewport(GetLeft(rSurface), GetBottom(rSurface), GetRight(rSurface), GetTop(rSurface));
			Debug.Assert(RenderException.CheckErrors() == true);
		}
		
		/// <summary>
		/// Get viewport width respect a RenderSurface. 
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> on which the viewport is applied.
		/// </param>
		/// <returns>
		/// A <see cref="System.Int32"/> indicating the RenderViewport width
		/// in pixels.
		/// </returns>
		public int GetWidth(RenderSurface rSurface)
		{
			return ((int)((float)rSurface.Width * (mRight-mLeft)));
		}
		
		/// <summary>
		/// Get viewport height respect a RenderSurface. 
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Int32"/> indicating the RenderViewport height
		/// in pixels.
		/// </returns>
		public int GetHeight(RenderSurface rSurface)
		{
			return ((int)((float)rSurface.Height * (mTop-mBottom)));
		}
		
		/// <summary>
		/// Get viewport aspect ratio respect a RenderSurface. 
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/>
		/// </param>
		/// <returns>
		/// A <see cref="System.Single"/>
		/// </returns>
		public float AspectRatio(RenderSurface rSurface)
		{
			return ((float)GetWidth(rSurface) / (float)GetHeight(rSurface));
		}

		/// <summary>
		/// Left viewport edge coordinate, in RenderSurface coordinates.
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> on which the viewport is applied.
		/// </param>
		/// <returns>
		/// It returns an integer coordinate of the viewport left edge.
		/// </returns>
		public int GetLeft(RenderSurface rSurface)
		{
			return ((int)((float)GetWidth(rSurface)*Left));
		}
		
		/// <summary>
		/// Right viewport edge coordinate, in RenderSurface coordinates.
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> on which the viewport is applied.
		/// </param>
		/// <returns>
		/// It returns an integer coordinate of the viewport right edge.
		/// </returns>
		public int GetRight(RenderSurface rSurface)
		{
			return ((int)((float)GetWidth(rSurface)*Right));
		}

		/// <summary>
		/// Bottom viewport edge coordinate, in RenderSurface coordinates.
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> on which the viewport is applied.
		/// </param>
		/// <returns>
		/// It returns an integer coordinate of the viewport bottom edge.
		/// </returns>
		public int GetBottom(RenderSurface rSurface)
		{
			return ((int)((float)GetHeight(rSurface)*Bottom));
		}

		/// <summary>
		/// Top viewport edge coordinate, in RenderSurface coordinates.
		/// </summary>
		/// <param name="rSurface">
		/// A <see cref="RenderSurface"/> on which the viewport is applied.
		/// </param>
		/// <returns>
		/// It returns an integer coordinate of the viewport top edge.
		/// </returns>
		public int GetTop(RenderSurface rSurface)
		{
			return ((int)((float)GetHeight(rSurface)*Top));
		}
		
		/// <summary>
		/// Left viewport edge coordinate.
		/// </summary>
		public float Left { get { return (mLeft); } }
		
		/// <summary>
		/// Right viewport edge coordinate. 
		/// </summary>
		public float Right { get { return (mRight); } }
		
		/// <summary>
		/// Bottom viewport edge coordinate. 
		/// </summary>
		public float Bottom { get { return (mBottom); } }
		
		/// <summary>
		/// Top viewport edge coordinate. 
		/// </summary>
		public float Top { get { return (mTop); } }
		
		/// <summary>
		/// Left viewport edge coordinate.
		/// </summary>
		private float mLeft;
		/// <summary>
		/// Right viewport edge coordinate. 
		/// </summary>
		private float mRight;
		/// <summary>
		/// Bottom viewport edge coordinate. 
		/// </summary>
		private float mBottom;
		/// <summary>
		/// Top viewport edge coordinate. 
		/// </summary>
		private float mTop;
	}
}
